package com.pms.utils;


import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;


import com.pms.exceptions.ErrorCode;
import com.pms.exceptions.StudioException;


public class CalendarUtility {
	public final static String	TRACE_ID	= "CalendarUtility";

	// Array code added by Amit

	public static boolean compareDateWithCurrentDate(int day, int month, int year) {
		Calendar fromDate = Calendar.getInstance();
		fromDate.set(Calendar.DATE, day);
		fromDate.set(Calendar.MONTH, month - 1);
		fromDate.set(Calendar.YEAR, year);
		fromDate.set(Calendar.HOUR, 00);
		fromDate.set(Calendar.MINUTE, 00);
		fromDate.set(Calendar.SECOND, 00);
		fromDate.set(Calendar.MILLISECOND, 00);

		Calendar currentDate = Calendar.getInstance();
		currentDate.set(Calendar.HOUR, 00);
		currentDate.set(Calendar.MINUTE, 00);
		currentDate.set(Calendar.SECOND, 00);
		currentDate.set(Calendar.MILLISECOND, 00);

		int value = currentDate.compareTo(fromDate);
		if (value == 0) {
			return true;
		} else if (value > 0) {
			return false;
		} else if (value < 0) {
			return true;
		} else {
			return true;
		}
	}

	public static boolean compareFromDateWithReturnDate(int fromDay, int fromMonth, int fromYear,
			int returnDay, int returnMonth, int returnYear) {
		Calendar fromDate = Calendar.getInstance();
		fromDate.set(Calendar.DATE, fromDay);
		fromDate.set(Calendar.MONTH, fromMonth - 1);
		fromDate.set(Calendar.YEAR, fromYear);
		fromDate.set(Calendar.HOUR, 00);
		fromDate.set(Calendar.MINUTE, 00);
		fromDate.set(Calendar.SECOND, 00);
		fromDate.set(Calendar.MILLISECOND, 00);

		Calendar returnDate = Calendar.getInstance();
		returnDate.set(Calendar.DATE, returnDay);
		returnDate.set(Calendar.MONTH, returnMonth - 1);
		returnDate.set(Calendar.YEAR, returnYear);
		returnDate.set(Calendar.HOUR, 00);
		returnDate.set(Calendar.MINUTE, 00);
		returnDate.set(Calendar.SECOND, 00);
		returnDate.set(Calendar.MILLISECOND, 00);

		// System.out.println("From Date Is :" +
		// (fromDate.getTime()).toString());
		// System.out.println("Return Date Is :" +
		// (returnDate.getTime()).toString());

		int value = fromDate.compareTo(returnDate);

		if (value == 0) {
			return false;
		} else if (value > 0) {
			// System.out.println("Return Date Should be higher than From
			// Date");
			return true;
		} else if (value < 0) {
			return false;
		} else {
			return false;
		}
	}

	/**
	 * Return a Calendar with the current date & time and the system base
	 * TimeZone (UTC).
	 * 
	 */
	public static Calendar now() {
		return GregorianCalendar.getInstance(mBaseTimeZone);
	}

	/**
	 * Return a Calendar with the current date & time and the parameter
	 * specified TimeZone.
	 * 
	 */
	public static Calendar now(TimeZone aTimeZone) {
		return GregorianCalendar.getInstance(aTimeZone);
	}

	/**
	 * Creates a Calendar, with UTC Timezone, from the time specified by a Date
	 * argument.
	 * 
	 * @param aDate
	 *            converted to the Calendar
	 * 
	 * @return Calendar converted from the Date
	 * 
	 */
	public static Calendar createCalendar(Date aDate) {

		if (aDate == null)
			return null;

		GregorianCalendar calendar = new GregorianCalendar(mBaseTimeZone);
		calendar.setTime(aDate);

		return calendar;
	}

	/**
	 * compare two calendars down to Date level only
	 * 
	 * @param aCalendar
	 *            first Calendar
	 * @param aCalendar
	 *            second Calendar
	 * @param aRoundType
	 *            String rounding flag - 'CEIL' or null/empty string
	 * 
	 * @return int return 0 if calendar date are equal, less than 0 if first
	 *         after the second, more than 0 if frist before the second.
	 * 
	 */
	public static long compareDate(Calendar aCalendar1, Calendar aCalendar2, String aRoundType) {

		int roundT = -1;
		if (aRoundType != null && aRoundType.equals("CEIL")) {
			roundT = 1;
		}
		if (aCalendar1 != null && aCalendar2 != null) {
			GregorianCalendar c1 = new GregorianCalendar(aCalendar1.get(Calendar.YEAR), aCalendar1
					.get(Calendar.MONTH), aCalendar1.get(Calendar.DATE), 0, 0, 0);
			GregorianCalendar c2 = new GregorianCalendar(aCalendar2.get(Calendar.YEAR), aCalendar2
					.get(Calendar.MONTH), aCalendar2.get(Calendar.DATE) + roundT, 0, 0, 0);

			double m1 = c1.getTime().getTime();
			double m2 = c2.getTime().getTime();
			double duration = m2 - m1;
			duration = Math.ceil(duration / 60000);
			duration = Math.ceil(duration / 60);
			duration = Math.ceil(duration / 24);

			return (long) duration;
		}
		return -1;
	}

	/**
	 * compare two calendars down to Date level only
	 * 
	 * @param aCalendar
	 *            first Calendar
	 * @param aCalendar
	 *            second Calendar
	 * 
	 * @return int return 0 if calendar date are equal, greater than 0 if first
	 *         after the second, less than 0 if frist before the second.
	 * 
	 */
	public static long compareDate(Calendar aCalendar1, Calendar aCalendar2) {
		return compareDate(aCalendar1, aCalendar2, null);
	}

	/**
	 * Utility function to convert Calendars to the start of the month they are
	 * currently set to offset by a number of months. The offset may be 0 for
	 * the current month, positive to add months or negative to subtract months.
	 * 
	 * It returns a Calendar representing the start of the calculated month.
	 * 
	 * String parsing is a hack to eliminate time and day from the Calendar in a
	 * timezone safe way.
	 * 
	 * If zero is add the returned Calendar wil be the start of the month in the
	 * supplied Calendar.
	 * 
	 * @param UserToken
	 *            a User connection context object
	 * @param Calnedar
	 *            the date to base the start of month date on
	 * @param int
	 *            a positive or negative number of months to add or subtract, if
	 *            its zero the method returns the start of the month in the
	 *            Calendar supplied.
	 * 
	 * @return Calendar the Calendar representing the start of a month
	 * 
	 * @throws BobeException
	 *             if there is a parsing error
	 */
	public static Calendar toStartOfMonth(Calendar aCalendar, int monthsToAddOrSubtract)
	throws StudioException {

		try {
			Formatter formatter = CalendarUtility.getFormatter();
			int year = Integer.parseInt(formatter.formatCustomTime(aCalendar, "yyyy"));
			int month = Integer.parseInt(formatter.formatCustomTime(aCalendar, "MM"));
			int totalMonths = (year * 12) + month + monthsToAddOrSubtract;
			year = (int) totalMonths / 12;
			month = totalMonths % 12;
			String newMonthString = Integer.toString((year * 100) + month);
			Calendar newMonth = formatter.parseCustomTime(newMonthString, "yyyyMM");
			return newMonth;
		} catch (ParseException e) {
			throw new StudioException(ErrorCode.GENERIC_ERROR, "Error Parsing Date");
		}
	}

	/**
	 * Utility function to convert Calendars to the start of the day they are
	 * currently set to offset by a number of days. The offset may be 0 for the
	 * current month, positive to add days or negative to subtract days.
	 * 
	 * It returns a Calendar representing the start of the calculated day.
	 * 
	 * String parsing is a hack to eliminate time from the Calendar in a
	 * timezone safe way.
	 * 
	 * If zero is add the returned Calendar wil be the start of the month in the
	 * supplied Calendar.
	 * 
	 * @param UserToken
	 *            a User connection context object
	 * @param Calnedar
	 *            the date to base the start of day date on
	 * @param int
	 *            a positive or negative number of days to add or subtract, if
	 *            its zero the method returns the start of the day in the
	 *            Calendar supplied.
	 * 
	 * @return Calendar the Calendar representing the start of a month
	 * 
	 * @throws BobeException
	 *             if there is a parsing error
	 */
	public static Calendar toStartOfDay(Calendar aCalendar, int daysToAddOrSubtract) throws StudioException {

		try {
			SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy");
			String dateString = format.format(aCalendar.getTime());
			dateString = dateString + " 00:00:00";
			format = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
			Calendar newDate = CalendarUtility.createCalendar(format.parse(dateString));
			newDate.add(Calendar.DATE, daysToAddOrSubtract);			
			return newDate;
		} catch (ParseException e) {
			throw new StudioException(ErrorCode.GENERIC_ERROR, "Error Parsing Date");
		}
	}

	public static Date getDateForFormat(String aDateString, SimpleDateFormat aFormat) throws Exception {
		try {
			return aFormat.parse(aDateString);
		} catch (Exception e) {

			throw new Exception("Invalid Date - " + aDateString);
		}
	}

	public static int getHoursBetween (java.util.Calendar startTime, java.util.Calendar endTime) {
		long differenceInMillis = endTime.getTimeInMillis() - startTime.getTimeInMillis();
		double temp = differenceInMillis /(60*60*1000);
		int differenceInHours = new Double(temp).intValue();
		//System.out.println("----- Difference in Hours --------" + differenceInHours);
		return differenceInHours;
	}

	public static int getMinutesBetween (java.util.Calendar startTime, java.util.Calendar endTime) {
		int diffInHours = getHoursBetween(startTime,endTime);
		startTime.add(Calendar.HOUR,diffInHours);
		long differenceInMillis = endTime.getTimeInMillis() - startTime.getTimeInMillis();
		int differenceInHours = new Double(differenceInMillis /(60*1000)).intValue();
		//System.out.println("----- Difference in Minutes --------" + differenceInHours);
		return differenceInHours;
	}

	/**
	 * Calculates the number of days between two calendar days in a manner which
	 * is independent of the Calendar type used.
	 * 
	 * @param d1
	 *            The first date.
	 * @param d2
	 *            The second date.
	 * 
	 * @return The number of days between the two dates. Zero is returned if the
	 *         dates are the same, one if the dates are adjacent, etc. The order
	 *         of the dates does not matter, the value returned is always >= 0.
	 *         If Calendar types of d1 and d2 are different, the result may not
	 *         be accurate.
	 */
	public static int getDaysBetween(java.util.Calendar d1, java.util.Calendar d2) {
		if (d1.after(d2)) { // swap dates so that d1 is start and d2 is end
			java.util.Calendar swap = d1;
			d1 = d2;
			d2 = swap;
		}
		int days = d2.get(java.util.Calendar.DAY_OF_YEAR) - d1.get(java.util.Calendar.DAY_OF_YEAR);
		int y2 = d2.get(java.util.Calendar.YEAR);
		if (d1.get(java.util.Calendar.YEAR) != y2) {
			d1 = (java.util.Calendar) d1.clone();
			do {
				days += d1.getActualMaximum(java.util.Calendar.DAY_OF_YEAR);
				d1.add(java.util.Calendar.YEAR, 1);
			} while (d1.get(java.util.Calendar.YEAR) != y2);
		}
		return days;
	} // getDaysBetween()

	public static long daysBetween(Date d1, Date d2) {
		return ((d2.getTime() - d1.getTime() + ONE_HOUR) / (ONE_HOUR * 24));
	}

	// ----------------------------------------------------------
	private static final TimeZone	mBaseTimeZone	= TimeZone.getTimeZone("Asia/Calcutta");

	private static final long		ONE_HOUR		= 60 * 60 * 1000L;


	/** Converts a java.sql.Timestamp to a Calendar.
	 * java.sql.Timestamp is used so Oracle will save both date and time components.
	 *
	 * @param aTimestamp  Timestamp to convert to a Calendar
	 *
	 * @return Calendar converted from the Timestamp, with UTC Timezone set
	 *
	 */
	public static Calendar fromSQLTimestamp(java.sql.Timestamp aTimestamp) {

		if(aTimestamp == null)
			return null;

		Calendar calendar = new GregorianCalendar( mBaseTimeZone );
		calendar.setTime(new Date(aTimestamp.getTime()));
		return calendar;
	}

	/** Converts a Calendar to a java.sql.Timestamp.
	 * java.sql.Timestamp is used so Oracle will save both date and time components.
	 *
	 * @param Calendar converted to the Timestamp, with UTC Timezone set
	 *
	 * @return aTimestamp  Timestamp from convert to a Calendar
	 *
	 */
	public static java.sql.Timestamp toSQLTimestamp(Calendar aCalendar) {
		return (aCalendar == null) ? null : new java.sql.Timestamp(aCalendar.getTime().getTime());
	}


	public static Formatter getFormatter(){
		Locale locale =new Locale("en", "US");

		TimeZone timeZone  = new SimpleTimeZone(
				36000000,           // offset
				"TravisCustom/Asia/Calcutta", // ID
				// daylight savings settings
				9,                  // startMonth - start month  October (this was wrongly August in our version of Solaris Java)
				-1,                 // startDay - last valid startDayOfWeek in October
				1,                  // startDayOfWeek - Sunday
				2 * 60 * 60 * 1000, // startTime - 2 AM
				2,                  // endMonth - March
				-1,                 // endDay - last valid endDayOfWeek in March
				1,                  // endDayOfWeek - Sunday
				3 * 60 * 60 * 1000, // endTime - end time - 3 AM
				1 * 60 * 60 * 1000  // daylight savings offset - + 1 hour
		);

		// now that we have set out TimeZone overrides
		// this sort of semi-recursive call resets the default time zone,
		// using the TimeZone ID of JVMs default TimeZone, but re-evaluated
		// through by this manager in case we want to replace the TimeZone
		// associated with that default.
		// In Bobe we try to avoid using the default TimeZone,
		// using the TimeZone(s) associated with a SiteView instead.
		// However some code like the Trace class operate outside of a SiteView
		// context and use the default. Also some buggy code uses the default
		// and gets away with it because there is effectively only one
		// TimeZone used on most sites.
		//TimeZone.setDefault(getTimeZone(TimeZone.getDefault().getID()));
		//TimeZone.setDefault(getTimeZone(TimeZone.getDefault().getID()));
		TimeZone.setDefault(TimeZone.getTimeZone("Asia/Calcutta"));


		Formatter formatter = new Formatter();

		DateFormat dateFormat;

		dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT,
				DateFormat.SHORT, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setShortDateTimeFormat(dateFormat);

		dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
				DateFormat.MEDIUM, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setMediumDateTimeFormat(dateFormat);

		dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG,
				DateFormat.LONG, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setLongDateTimeFormat(dateFormat);

		dateFormat = DateFormat.getDateInstance(DateFormat.SHORT, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setShortDateFormat(dateFormat);

		dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setMediumDateFormat(dateFormat);

		dateFormat = DateFormat.getDateInstance(DateFormat.LONG, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setLongDateFormat(dateFormat);

		dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setShortTimeFormat(dateFormat);

		dateFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setMediumTimeFormat(dateFormat);

		dateFormat = DateFormat.getTimeInstance(DateFormat.LONG, locale);
		dateFormat.setTimeZone(timeZone);
		formatter.setLongTimeFormat(dateFormat);

		dateFormat = new SimpleDateFormat(Formatter.SORTABLE_DATE_TIME_FORMAT);
		dateFormat.setTimeZone(timeZone);
		formatter.setSortableDateFormat(dateFormat);

		dateFormat = new SimpleDateFormat(Formatter.SORTABLE_DATE_TIME_FORMAT);
		dateFormat.setTimeZone(timeZone);
		formatter.setSortableDateFormat(dateFormat);

		formatter.setNumberFormat(NumberFormat.getNumberInstance(locale));

		// we need to manually control the currency symbol
		// therefore our currency NumberFormat is only a standard
		// NumberFormat with the number of decimal places set according to
		// from the currency NumberFormat for the same Locale
		NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
		NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(locale);
		// numberFormat.setMaximumFractionDigits(0); // matt hacked this for no
		// decimal places
		// numberFormat.setMinimumFractionDigits(0);
		numberFormat.setMaximumFractionDigits(currencyFormat
				.getMaximumFractionDigits());
		numberFormat.setMinimumFractionDigits(currencyFormat
				.getMinimumFractionDigits());
		formatter.setCurrencyFormat(numberFormat);

		// set up a plain double formatter which formats 2 decimal places but
		// no thousands seperators
		numberFormat = NumberFormat.getNumberInstance(locale);
		currencyFormat = NumberFormat.getCurrencyInstance(locale);
		numberFormat.setMaximumFractionDigits(currencyFormat
				.getMaximumFractionDigits());
		numberFormat.setMinimumFractionDigits(currencyFormat
				.getMinimumFractionDigits());
		numberFormat.setGroupingUsed(false);
		formatter.setPlainCurrencyFormat(numberFormat);

		return formatter;
	}

	/**
	 * This method returns the Date object given time and the Date
	 * @param Time hh:mm aaa
	 * @param date Date Object
	 * @return Date 
	 */
	public static Date fromTimeToDate(String arrivalTime, Date date) {

		SimpleDateFormat sdf1   = new SimpleDateFormat("yyyy-MM-dd");
		String departureTime = sdf1.format(date);
		departureTime = departureTime +" " + arrivalTime;
		SimpleDateFormat sdf2   = new SimpleDateFormat("yyyy-MM-dd hh:mm aaa");

		try {
			date = sdf2.parse(departureTime);
		} catch (ParseException e) {
			//System.out.println("@@@@@ DepartureTime 1st : "+departureTime);
			try {
				sdf2   = new SimpleDateFormat("yyyy-MM-dd hh:mmaaa");
				date = sdf2.parse(departureTime);

			} catch (ParseException e1) {
				//System.out.println("@@@@@ DepartureTime 2nd : "+departureTime);
				try {
					sdf2   = new SimpleDateFormat("yyyy-MM-dd hh.mmaaa");
					date = sdf2.parse(departureTime);
				} catch (ParseException e2) {
					//System.out.println("@@@@@ DepartureTime 3rd : "+departureTime);
					try {
						sdf2   = new SimpleDateFormat("yyyy-MM-dd hh.mm aaa");
						date = sdf2.parse(departureTime);
					} catch (ParseException e3) {

						// System.out.println("@@@@@ DepartureTime 4th : "+departureTime);
						try {
							sdf2   = new SimpleDateFormat("yyyy-MM-dd hh:.mm aaa");
							date = sdf2.parse(departureTime);
						} catch (ParseException e4) {
							// System.out.println("@@@@@ DepartureTime 5th : "+departureTime);
							try {
								sdf2   = new SimpleDateFormat("yyyy-MM-dd hh:.mmaaa");
								date = sdf2.parse(departureTime);
							} catch (ParseException e5) {
								//	 System.out.println("@@@@@ DepartureTime 6th : "+departureTime);
								e.printStackTrace();
							}

						}
					}

				}

			}
		}



		return (date);
	}
	public static Date getDate(String strDepartureDate){
		strDepartureDate = strDepartureDate + " 00:00:00";

		SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

		Date departureDate = null;

		try {
			departureDate = CalendarUtility.getDateForFormat(strDepartureDate,format);
		} catch (Exception e) {
			departureDate = new Date();
		}
		strDepartureDate = departureDate.toString();	
		format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dDate = format.format(departureDate);

		System.out.println("********************* Final Date String is ::: " + dDate + "Hours : " + departureDate.getHours() + "Minutes :" + departureDate.getMinutes() + "Seconds :" + departureDate.getSeconds());
		return departureDate;
	}
	public static Date getDate(String strDepartureDate,String time){
		strDepartureDate = strDepartureDate +" "+time;

		SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");

		Date departureDate = null;

		try {
			departureDate = CalendarUtility.getDateForFormat(strDepartureDate,format);
		} catch (Exception e) {
			departureDate = new Date();
		}
		strDepartureDate = departureDate.toString();	
		format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dDate = format.format(departureDate);

		System.out.println("********************* Final Date String is ::: " + dDate + "Hours : " + departureDate.getHours() + "Minutes :" + departureDate.getMinutes() + "Seconds :" + departureDate.getSeconds());
		return departureDate;
	}
	public static Timestamp generateDate(String strDate) throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        Date dateObj = sdf.parse(strDate);
        Timestamp fromDate = new Timestamp(dateObj.getTime());
        
        return fromDate;
	}
	
	public static Time stringToTime(String time) throws Exception{
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
		Time timeOb = new Time(sdf.parse(time).getTime());
		return timeOb;
	}
	
	public static String getCurrentTime() {
		Calendar currTime = new GregorianCalendar();
		
		Date newDate = new Date();
		
		currTime.setTime(newDate);
		
		int hour = currTime.get(Calendar.HOUR_OF_DAY);
		int minutes = currTime.get(Calendar.MINUTE);
		int seconds = currTime.get(Calendar.SECOND);
		
		String strCurTime = hour + ":" + minutes + ":" + seconds;
		
		return strCurTime;
	}
	
	public static Time convert12Hourto24Hour(String timeIn12Hour) throws Exception {
		SimpleDateFormat time12HrFormat = new SimpleDateFormat("hh:mm a");
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
		
		Date time12HrObj = time12HrFormat.parse(timeIn12Hour);
		
		return CalendarUtility.stringToTime(sdf.format(time12HrObj));
	}
	
	public static double getTimeDifferenceInMins(Time startTime ,Time endTime) throws Exception {
		double mins = 0;
		
		long diffMins = endTime.getTime() - startTime.getTime();
		mins = (diffMins/1000)/60;
		
		return mins;
	}
	
	public static double getTimeDifferenceInHrs(Time startTime ,Time endTime) throws Exception {
		double hrs = 0;
		
		long diffHrs = endTime.getTime() - startTime.getTime();
		hrs = ((diffHrs/1000)/60)/24;
		
		return hrs;
	}
	
	/**
	 * E.g. <br> Date newDate = CalendarUtility.getNoOfDaysByPassedDate(new Date() , 2) will return 2 days after the current Date.
	 *      <br> Date oldDate = CalendarUtility.getNoOfDaysByPassedDate(new Date() , -2) will return 2 days before the current Date. 
	 * 
	 * @param date     --- Relative Date for which no of Days before or after date required.
	 * @param noOfDays --- No Of Days <br> noOfDays will be negative for old date from passed date. <br> noOfDays will be positive for new date from passed date.
	 * @return Date object.
	 */
	public static Date getNoOfDaysByPassedDate(Date date ,long noOfDays){
		long base = date.getTime();
		
		Date beforeDate = new Date(base+noOfDays*24*60*60*1000);  
		System.out.println(noOfDays + " days before: " + beforeDate);  
		
		return beforeDate;
	}
}



